package org.flashamaphone.tones
{
	/** 
	 * A tone extended to accept musical notations as the input to define it
	 */
	public class Note extends Tone
	{
		public static const C 		:String = "C";
		public static const CSHARP	:String = "C#";
		public static const DFLAT	:String = "Db";
		public static const D 		:String = "D";
		public static const DSHARP	:String = "D#";
		public static const EFLAT	:String = "Eb";
		public static const E 		:String = "E"; 
		public static const F 		:String = "F";
		public static const FSHARP	:String = "F#";
		public static const GFLAT  	:String = "Gb";
		public static const G		:String = "G";
		public static const GSHARP 	:String = "G#";
		public static const AFLAT	:String = "Ab";
		public static const A 		:String = "A"; 
		public static const ASHARP	:String = "A#";
		public static const	BFLAT	:String = "Bb";
		public static const B 		:String = "B"; 
		
		/** octave */
		protected var _octave:int;
		
		/** current note notation */
		protected var _currentnotation:String;
		
		/** sharp note notations */
		protected static const _sharpnotations:Array = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"];
		
		/** flat note notations */
		protected static const _flatnotations:Array  = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"];
		
		/** 
		 * constructor
		 * 
		 * @param note notation
		 * @param octave
		 * @param duration
		 */
		public function Note(value:String = "", octave:int = 4, duration:Number = 1)
		{
			super();
			this.octave = octave;
			notation = value;
			this.duration = duration;
			buildNote();
		}
		
		/** 
		 * get major notes in key signature using current note as root
		 * 
		 * @return keys in key signature
		 */
		public function get majorKeySignature():Array {
			return Note.notesInKeySignature(notation, true);
		}
		
		/** 
		 * get minor notes in key signature using current note as root
		 * 
		 * @return keys in key signature
		 */
		public function get minorKeySignature():Array {
			return Note.notesInKeySignature(notation, false);
		}
		
		/**
		 * get sharp notations
		 * 
		 * @return sharp notations
		 */
		public static function get sharpNotations():Array {
			return _sharpnotations.slice();
		}
		
		/**
		 * get flat notations
		 * 
		 * @return flat notations
		 */
		public static function get flatNotations():Array {
			return _flatnotations.slice();
		}
		
		/** 
		 * get notes in a specific key signature
		 * 
		 * @param key (root note)
		 * @param if major key signature
		 * @return keys in key signature
		 */
		public static function notesInKeySignature(key:String, major:Boolean = true):Array {
			var notesToIndex:Array;
			var notesInKey:Array = new Array();
			var startPos:int;
			
			// find the correct note and notation
			if (_sharpnotations.indexOf(key) != -1) {
				notesToIndex = _sharpnotations.slice();
				startPos = _sharpnotations.indexOf(key);
			} else {
				notesToIndex = _flatnotations.slice();
				startPos = _flatnotations.indexOf(key);
			}
			
			// double the array length
			var len:int = notesToIndex.length;
			for ( var c:int = 0; c < len; c++ ) {
				notesToIndex.push(notesToIndex[c]);
			}
			// chop off the front of the array to start at the root key in the key signature
			notesToIndex.splice(0, startPos);
			
			// build the key signature
			if (major) {
				// MAJOR From root: whole step, whole step, half step, whole step, whole step, whole step, half step
				notesInKey.push( notesToIndex[0] );
				notesInKey.push( notesToIndex[2] );
				notesInKey.push( notesToIndex[4] );
				notesInKey.push( notesToIndex[5] );
				notesInKey.push( notesToIndex[7] );
				notesInKey.push( notesToIndex[9] );
				notesInKey.push( notesToIndex[11] );
			} else {
				// MINOR From root: whole step, half step, whole step, whole step, half step, whole step, whole step
				notesInKey.push( notesToIndex[0] );
				notesInKey.push( notesToIndex[2] );
				notesInKey.push( notesToIndex[3] );
				notesInKey.push( notesToIndex[5] );
				notesInKey.push( notesToIndex[7] );
				notesInKey.push( notesToIndex[8] );
				notesInKey.push( notesToIndex[10] );
			}	
			return notesInKey;
		}
		
		/**
		 * notation setter
		 * 
		 * @param note notation
		 */
		public function set notation(value:String):void {
			_currentnotation = value;
			buildNote();
		}
		
		/**
		 * notation getter
		 * 
		 * @return note notation
		 */
		public function get notation():String {
			return _currentnotation;
		}
		
		/**
		 * octave setter
		 * 
		 * @param octave
		 */
		public function set octave(value:int):void {
			_octave = value;
			buildNote();
		}
		
		/**
		 * octave getter
		 * 
		 * @return octave
		 */
		public function get octave():int {
			return _octave;
		}
		
		
		/** 
		 * setter for the current note notation
		 * 
		 * @param value
		 */
		public function buildNote():void {
			var indx:int = _sharpnotations.indexOf(_currentnotation);
			
			if (indx == -1) {
				indx = _flatnotations.indexOf(_currentnotation);
			}
			
			if (indx != -1) {
				//adjust index for octave
				indx += (octave-4) * _sharpnotations.length;
				frequency = 440 * (Math.pow(2, indx/12));
			}
		}
	}
}